package com.pudu.pudusclient;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.pudu.pdrobot.App;

import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.LinkedBlockingQueue;

public class RobotSetService extends Service {
    public RobotSetService() {
    }

    public final static int RBTS_PORT = 5000;

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return binder;
    }

    private IBinder binder = new SetsBinder();

    public class SetsBinder extends Binder {
        public RobotSetService getService() {
            return RobotSetService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i(App.MYTAG, "RobotSetService.onDestroy");
        super.onDestroy();
    }

    @Override
    public void onCreate() {
        Log.i(App.MYTAG, "RobotSetService.onCreate");
        mHandler = new MyHandler(this);

        StartMyServer();
    }

    MyHandler mHandler;
    private static class MyHandler extends Handler {
        private final WeakReference<RobotSetService> mServices;

        public MyHandler(RobotSetService service) {
            mServices = new WeakReference<RobotSetService>(service);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x01:
                    break;
            }
        }
    }

    ServerSocket mServer;
    boolean bIsWorking = false;

    Socket mClient;
    Handler mControlHandler;
    HandlerThread mControlHandlerThread;
    TLSSendRunnable mSendRunnable;
    TLSReceiveRunnable mReceiveRunnable;

    Thread mSendThread;
    Thread mReceiveThread;
    private void StartMyServer(){
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    mServer = new ServerSocket(RBTS_PORT);

                    while(true)
                    {
                        Socket client = mServer.accept();
                        if( mClient == null )
                        {
                            mClient = client;

                            mClient.setSoTimeout(1000);
                            mClient.setKeepAlive(true);

                            //setup control thread
                            ClientControlCallback controlcallback = new ClientControlCallback();
                            mControlHandlerThread = new HandlerThread("ClientSocketThread" + client);
                            mControlHandlerThread.start();
                            mControlHandler = new Handler(mControlHandlerThread.getLooper(), controlcallback);

                            //setup send thread
                            mSendRunnable = new TLSSendRunnable(client);
                            mSendThread = new Thread(mSendRunnable);
                            mSendThread.start();

                            //setup receive thread
                            mReceiveRunnable = new TLSReceiveRunnable(client);
                            mReceiveThread = new Thread(mReceiveRunnable);
                            mReceiveThread.start();

                            //set work flag
                            synchronized (RobotSetService.this) {
                                bIsWorking = true;
                            }

                            //set up heartbeat
//                            Message msg = mControlHandler.obtainMessage();
//                            msg.what = 0x01;
//                            mControlHandler.sendMessageDelayed(msg, 1000);

                        }
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void Send(byte[] data) {
        boolean isWorking = false;
        synchronized (RobotSetService.this) {
            isWorking = bIsWorking;

            if (isWorking == false)
                return;

            if (mSendRunnable != null) {
                mSendRunnable.enqueueSocketPacket(new MySocketPacket(data, 0x2));
            }
        }
        return;
    }

    public void Send(String data) {
        boolean isWorking = false;
        synchronized (RobotSetService.this) {
            isWorking = bIsWorking;

            if (isWorking == false)
                return;

            if (mSendRunnable != null) {
                mSendRunnable.enqueueSocketPacket(new MySocketPacket(data.getBytes(), 0x2));
            }
        }
    }

    public class ClientControlCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {

            switch (msg.what) {
                case 0x01:
                    RobotSetService.this.Send("server heart".getBytes());

                    Message mymsg = mControlHandler.obtainMessage();
                    mymsg.what = 0x01;
                    mControlHandler.sendMessageDelayed(mymsg, 1000);
                    break;
                case 0x02:
                    boolean isWorking = false;
                    synchronized (RobotSetService.this) {
                        isWorking = bIsWorking;
                    }

                    try {
                        if (isWorking == true) {
                            mReceiveThread.interrupt();
                            mSendThread.interrupt();

                            try {
                                mClient.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                            mControlHandlerThread.quit();

                            mClient = null;
                        }
                    }catch(Exception e)
                    {
                        e.printStackTrace();
                    }

                    break;

            }
            return false;
        }
    }

    class TLSReceiveRunnable implements Runnable {
        Socket mSocket;

        TLSReceiveRunnable(Socket socket) {
            mSocket = socket;
        }

        @Override
        public void run() {

            InputStream inputReader = null;
            byte[] result = new byte[1024];
            while (mSocket.isConnected() && !Thread.interrupted()) {

                try {
                    inputReader = mSocket.getInputStream();
                    int len = inputReader.read(result);
                    String strData = new String(result, 0, len);
                    if( strData != null && strData.isEmpty() == false) {
                        Log.i("kkkkk", strData);

                        try {
                            JSONObject myData = new JSONObject(strData);

                            String myType = myData.getString("type");
                            if(myType == null || myType.isEmpty() == true)
                            {
                                return;
                            }

                            if(myType.compareTo("ask") == 0) {
                                OnReceiveAsk();
                            }else if(myType.compareTo("custom") == 0){
                                OnReceiveCustom(strData);
                            }
                        }
                        catch(Exception e)
                        {
                            e.printStackTrace();
                        }
                    }

                } catch (SocketTimeoutException e) {
                    continue;
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }
            }

            Message msg = mControlHandler.obtainMessage();
            msg.what = 0x02;
            mControlHandler.sendMessage(msg);

            Log.i("aaaa", "ReceiveRunnable quit");
        }
    }

    void OnReceiveAsk()
    {
        String strTemplateData = BuildTemplateData();
        Send(strTemplateData);
    }

    void OnReceiveCustom(String strReceiveData)
    {

    }

    String BuildTemplateData()
    {
        String data = "";

        CustomTemplate myTemplate = CustomTemplate.getInstance();

        data = myTemplate.BuildTemplateData();

        return data;
    }

    class TLSSendRunnable implements Runnable {
        Socket mSocket;

        private LinkedBlockingQueue<MySocketPacket> sendingPacketQueue;

        TLSSendRunnable(Socket socket) {
            mSocket = socket;
        }

        public void enqueueSocketPacket(final MySocketPacket socketPacket) {
            try {
                getSendingPacketQueue().put(socketPacket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        protected LinkedBlockingQueue<MySocketPacket> getSendingPacketQueue() {
            if (sendingPacketQueue == null) {
                sendingPacketQueue = new LinkedBlockingQueue<MySocketPacket>();
            }
            return sendingPacketQueue;
        }

        @Override
        public void run() {

            MySocketPacket packet;
            try {
                while (!Thread.interrupted() && mSocket.isConnected() &&
                        (packet = getSendingPacketQueue().take()) != null) {

                    InetAddress address = mSocket.getInetAddress();

                    if(packet.getType() == 0x1) {
                        if (address.isReachable(1000) == true) {
                            Log.i("aaaa", "isReachable true. ");
                        } else {
                            Log.i("aaaa", "isReachable fail. ");
                            throw new Exception("isReachable failed");
                        }
                    }

                    {
                        OutputStream out = mSocket.getOutputStream();
                        out.write(packet.getData());
                        out.flush();
                        Writer myout = new OutputStreamWriter(out);
                        myout.flush();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            Message msg = mControlHandler.obtainMessage();
            msg.what = 0x02;
            mControlHandler.sendMessage(msg);

            Log.i("aaaa", "TLSSendRunnable quit");
        }
    }
}
